<?php
declare(strict_types=1);
/**
 * 申通API中心
 */
namespace Service\VenderClient\WayBillExpressCompanyApi;
use Service\Code\Normal;
use Service\BaseService;
use Service\ExtraService\TaskWorker;
use Service\HttpServer\Http;
class ZtoCompanyApiClient extends BaseService
{
    protected $config;
    const API_URL = 'https://izop.zt-express.com/oms/api';
    public function __construct( array $apiBody = [] )
    {
        $this->config = $apiBody;
    }
    
    public function logisticDeclare( array $declareInfo = []){
        $receiveInfo = $declareInfo['receiveInfo'];
        $senderInfo  = $declareInfo['senderInfo'];
        $orderGoods  = $declareInfo['orderGoods'];
        $order       = $declareInfo['order'];
        $goods       = $declareInfo['goods'];
        $e_company   = $declareInfo['e_company'];
        $ReceiverAddress=$receiveInfo['ReceiverProvince'].$receiveInfo['ReceiverCity'].$receiveInfo['ReceiverCountry'].$receiveInfo['ReceiverAddress'];
        $weight=0;
        $intlOrderItemList = [];ini_set('serialize_precision', '16');
        foreach ($orderGoods as $item) {
            $intlOrderItemList[] = [
                'itemId'        => $item['goods_id'], //商品ID
                'itemName'      => $goods[$item['goods_id']]['name'], //商品名称
                'itemUnitPrice' => (float) $item['price_pay'], //商品价格
                'itemQuantity'  => $item['num'], //商品购买数量,内容不可为0
                //'itemRemark'    => $item['Mark'], //商品备注
                'currencyType'  => "142", //货币类型
                'itemUnit'      => $goods[$item['goods_id']]['unit_name'], //计量单位
            ];
            $weight= bcadd((string)$weight, (string)$goods[$item['goods_id']]['weight']);
        }
        $weight=round($weight/1000, 3);
        if($order['express_num']){
            return Normal::returnCode(Normal::FAIL, '中通不支持重推运单，请联系中通处理');
        }
        $data = [
            //'logisticsId'       => $order['express_num']??'',//中通运单号
            'orderId'           => $order['sn'],//订单编号 Y 同一个客户订单编号不能重复
            'shipper'           => $senderInfo['SenderName'],//发货人名称 Y
            'shipperProv'       => $senderInfo['SenderProvince'],//发货人省份 Y
            'shipperCity'       => $senderInfo['SenderCity'],//发货人市 Y
            'shipperDistrict'   => $senderInfo['SenderCountry'],//发货人区县 Y
            'shipperAddress'    => $senderInfo['SenderAddress'],//发货人详细地址 Y
            'shipperMobile'     => $senderInfo['SenderPhone'],//发货人手机 N 手机与电话二选一(建议填手机号码)
            'shipperCountry'    => '中国',//发货人所在国 Y
            'consignee'         => $receiveInfo['ReceiverName'],//收货人名称 Y 
            'consigneeProv'     => $receiveInfo['ReceiverProvince'],//收货人省份 Y 收货人省份必填正确(如:上海市),涉及是否能获取正确的派送中转内容
            'consigneeCity'     => $receiveInfo['ReceiverCity'],//收货人市 Y 
            'consigneeDistrict' => $receiveInfo['ReceiverCountry'],//收货人区县 Y
            //收货人地址 Y 如跨境涉及物流运单需推送口岸,口岸会检验收货人地址内容,
            //下单时收货人地址内容要求是收货人省+收货人市+收货人区县三个字段+详细地址组成完整内容保存到此字段(如: 江苏省苏州市张家港市南环路188号
            'consigneeAddress'  => $ReceiverAddress,
            'consigneeMobile'   => $receiveInfo['ReceiverPhone'],//收货人电话 Y 手机与电话二选一
            'consigneeCountry'  => '中国',//收货人所在国 Y 进口为中国
            'weight'            => $weight,//毛重 kg
            'idType'            => 1,//证件类型 Y 1-身份证 2-军官证 3-护照 4-其它
            'customerId'        => $order['buyer_idcard'],//证件号码 Y 身份证内容必须与收货人对应
            'ieType'            => "I",//进出口标志 Y I-进口, E-出口
            'stockFlag'         => "2",//订单贸易类型 Y 1-集货 2-备货,默认2-备货
            'customsCode'       => $this->config['customsCode'],//海关编号 Y/N 如跨境涉及物流运单推送口岸,备货业务必填且正确,参考数据字典口岸编码表
            'platformSource'    => $this->config['appCode'],//客户ID Y 由ZTO分配默认内容,接口对接时分配
            //'sortContent'       => isset($logisticsGeneralInfo['SortContent']) ? $logisticsGeneralInfo['SortContent'] : '',//大头笔 N用于国际进口打印电子面单
            'needBigMark'       =>1,
            'netWeight'         => $weight,//净重(kg) Y
            'shipType'          => 8,//运输方式 Y 
            //'warehouseCode'     => 'au002' //测试环境需加入此参数
        ];

        $billReqBO = [
            'quantity'    => 1, //数量,不可为0,对应总署包裹数
            'ecpCode'     => $e_company['eCompanyCode'], //电商平台代码(海关备案编号)
            'ecpName'     => $e_company['eCompanyName'], //电商平台名称(海关备案名称)
            'ecpCodeG'    => $e_company['eCompanyCode'], //电商平台代码(国检)
            'ecpNameG'    => $e_company['eCompanyName'], //电商平台名称(国检)
            'wrapType'    => 2, //包装种类
            'companyCode' => '',//真实的海关代码,杭州清关时必填(如:杭州下沙是2991,杭州下城是2928, 2992杭州保税物流中心(B型))
        ];

        
        $data['billEntity']        = $billReqBO;
        $data['intlOrderItemList'] = $intlOrderItemList;
        $timestamp   = self::getMillisecond();
        $requestData = $this->postData($timestamp, $data);
        
        $result = $this->execute('addBbcImportOrder',$requestData,$timestamp);
        if ( $result['code'] != Normal::SUC ) {
            return $result;
        }
        $resonse= json_decode($result['data']['data'],true);
        return Normal::returnCode(Normal::SUC, "运单号申报成功", [
            'waybillNo'          => $resonse['logisticsId']??'',//运单号
            'bigWord'            => $resonse['sortContent']??'',//大字/三段码
            'packagePlace'       => $resonse['bagAddress']??'',//集包地
            'originData'         => json_encode($result['data'],JSON_UNESCAPED_UNICODE),//返回的原始数据result字符串
        ]);
    }
    
    /**
     * @desc 请求数据
     * @param $timestamp
     * @param $data
     * @return string
     */
    public function postData($timestamp, $data) {
        $sign        = $this->getSign($timestamp, json_encode($data, 320));
        //$data        = str_replace('"', '\\"', json_encode($data, JSON_UNESCAPED_UNICODE));
        //$postData    = '{"data":"' . $data . '","sign":"' . $sign . '"}';    print_r($postData);exit();  
        $postData=['data'=>json_encode($data, 320),'sign'=>$sign];        
        $requestData = $this->encrypt(json_encode($postData, 320));
        return $requestData;
    }

    /**
     * @desc 获取签名
     * @param $timestamp
     * @param $data
     * @return bool|string
     */
    public function getSign($timestamp, $data) {
        $sign = $timestamp . $this->config['secretKey'] . $data;
        $sign = preg_replace_callback('/\\\\u([0-9a-fA-F]{4})/', function ($match) {
            return mb_convert_encoding(pack('H*', $match[1]), 'UTF-8', 'UCS-2BE');
        }, $sign);
        return md5($sign);
    }

    /**
     * 计算IV
     * @return string
     */
    public function iv() {
        $ivArr = [0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF];
        $iv    = '';
        foreach ($ivArr as $value) {
            $iv .= chr($value);
        }
        return $iv;
    }

    /**
     * 加密消息体
     * @param $text
     * @return string
     */
    public function encrypt($text) {      
        return base64_encode(openssl_encrypt($text, 'DES-CBC', $this->config['secretKey'], OPENSSL_RAW_DATA, $this->iv()));
    }

    /**
     * @desc 解密
     * @param $data
     * @return string
     */
    public function decrypt($data) {
        return openssl_decrypt(base64_decode($data), 'DES-CBC', $this->config['secretKey'], OPENSSL_RAW_DATA, $this->iv());
    }

    /**
     * 返回毫秒级时间戳
     * @return number
     */
    public static function getMillisecond() {
        list($s1, $s2) = explode(' ', microtime());
        return (string) sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000);
    }
    
    /**
     * 推送接口
     * @param string $method
     * @param array $body
     */
    public function execute(string $method , $body,string $timestamp)
    {
        $url = self::API_URL . "?method={$method}&timestamp={$timestamp}&appCode={$this->config['appCode']}";
        $parameter = [
            [
                'tag'       => 'zto_order_'.$method,
                'url'       => $url,
                'data'      => $body,
                'method'    => 'post',
                'format'    => 'json',
                'header'    => [
                ],
            ],
        ];
        $resTask    = TaskWorker::instance()->addTask($parameter);
        $resTask    = current($resTask);
        $response   = @json_decode($resTask['response']['result'],true);
        if (isset($response['success']) && $response['success'] == true) {
            $response['data']= $this->decrypt($response['data']);
            return Normal::returnCode(Normal::SUC,"提交运单申报成功",$response);
        }
        $errorMsg = json_encode($response,JSON_UNESCAPED_UNICODE);
        return Normal::returnCode(Normal::FAIL,"提交运单失败:[{$errorMsg}]");
    }
}
